package com.linkgie.galaxyframework.crypto;

import com.linkgie.galaxyframework.utils.Base58Encoding;
import com.linkgie.galaxyframework.utils.BinaryUtils;

/**
 * 带前缀(Prefix)与校验和(checksum)的 Base58 编码；
 * 
 * @author spring
 *
 */
public class Base58ChecksumEncoding {

	private static final int CHECKSUM_SIZE = 4;

	private HashFunc checksumHasher;

	public Base58ChecksumEncoding(HashFunc checksumHasher) {
		this.checksumHasher = checksumHasher;
	}

	/**
	 * 编码；
	 * 
	 * @param prefix
	 * @param payload
	 * @return
	 */
	public String encode(int prefix, byte[] payload) {
		int prefixPayloadSize = 4 + payload.length;

		// x2 hash；
		byte[] encodedPayloadBytes = new byte[prefixPayloadSize + CHECKSUM_SIZE];
		BinaryUtils.toBytes(prefix, encodedPayloadBytes, 0);
		System.arraycopy(payload, 0, encodedPayloadBytes, 4, payload.length);
		byte[] x2hash = checksumHasher.hashInRaw(checksumHasher.hashInRaw(encodedPayloadBytes, 0, prefixPayloadSize));

		// 获得4字节 checksum；
		System.arraycopy(x2hash, 0, encodedPayloadBytes, prefixPayloadSize, CHECKSUM_SIZE);

		return Base58Encoding.encode(encodedPayloadBytes);
	}

	/**
	 * 验证校验和；
	 * 
	 * @param encodedPayload Base58Check 编码的数据；
	 * @return 返回 true 表示校验和匹配；返回 false 表示校验和不匹配；
	 */
	public boolean verifyChecksum(String encodedPayload) {
		byte[] encodedChecksum = Base58Encoding.decode(encodedPayload);

		// x2 hash;
		byte[] x2hash = checksumHasher
				.hashInRaw(checksumHasher.hashInRaw(encodedChecksum, 0, encodedChecksum.length - CHECKSUM_SIZE));

		return BinaryUtils.equals(encodedChecksum, encodedChecksum.length - CHECKSUM_SIZE, x2hash, 0, CHECKSUM_SIZE);
	}

	/**
	 * 验证前缀以及校验和；
	 * 
	 * @param encodedPayload Base58Check 编码的数据；
	 * @param expectedPrefix 预期的前缀；
	 * @return 返回 true 表示校验和匹配；返回 false 表示校验和不匹配；
	 */
	public boolean verifyPrefixAndChecksum(String encodedPayload, int expectedPrefix) {
		byte[] encodedPayloadBytes = Base58Encoding.decode(encodedPayload);

		// 验证前缀；
		int prefix = BinaryUtils.toInt(encodedPayloadBytes);
		if (prefix != expectedPrefix) {
			return false;
		}

		// x2 hash;
		byte[] x2hash = checksumHasher.hashInRaw(
				checksumHasher.hashInRaw(encodedPayloadBytes, 0, encodedPayloadBytes.length - CHECKSUM_SIZE));

		return BinaryUtils.equals(encodedPayloadBytes, encodedPayloadBytes.length - CHECKSUM_SIZE, x2hash, 0,
				CHECKSUM_SIZE);
	}

	/**
	 * 解码返回前缀；
	 * 
	 * @param encodedPayload
	 * @return
	 */
	public static int decodePrefix(String encodedPayload) {
		byte[] encodedPayloadBytes = Base58Encoding.decode(encodedPayload);
		return BinaryUtils.toInt(encodedPayloadBytes);
	}

}
